
class Rowcol(tuple):
    """
    class Rowcol is a struct, whose instance variables are: row and col.
    Immutable, you can't assign any instance variable after initialized.
    """
    
    __slots__ = []

    def __new__(cls, row, col):
        return tuple.__new__(cls, ( row, col ))
    
    def __getrow(self): return self[0]
    row = property(__getrow, None, None)
    
    def __getcol(self): return self[1]
    col = property(__getcol, None, None)
    
    def __repr__(self):
        return "Rowcol(%d,%d)" % self

class NamedStrAttributeError(AttributeError):
    def __init__(self, message):
        AttributeError.__init__(self, message)

class ReferenceRange(object):
    """
    ReferenceRange is a range on some NamedStr instance.
    A struct whose instance variables are: namedstr, begin, and range.
    Immutable, you can't assign any instance variable after initialized.
    """
    
    __slots__ = [ '__namedstr', '__begin', '__len' ]
    
    def __init__(self, namedstr, begin, end):
        self.__namedstr = namedstr
        self.__begin = begin
        self.__len = end - begin
    
    def __getnamedstr(self): return self.__namedstr
    namedstr = property(__getnamedstr, None, None)
    
    def __getbegin(self): return self.__begin
    begin = property(__getbegin, None, None)
    
    def __getend(self): return self.__begin + self.__len
    end = property(__getend, None, None)
    
    def __nonzero__(self):
        return self.__len != 0
    
    def __len__(self):
        return self.__len
    
    def __repr__(self):
        return "ReferenceRange(%s,%d,%d)" % ( repr(self.__namedstr), self.__begin, self.__begin + self.__len )
    
    def to_index_str(self):
        """
        Returns a string which contains a name of string and a begin index and an end index on the string.
        """
        return "%s %d-%d" % ( self.__namedstr.name, self.__begin, self.__begin + self.__len )

    def to_rowcol(self):
        """
        Returns a pair of Rowcol instances. 
        The first Rowcol means row and col of a charactor at the begin of this range.
        The second Rowcol means the end of this range.
        """
        ns_get_rowcol_at = self.__namedstr.get_rowcol_at
        return ( ns_get_rowcol_at(self.__begin), ns_get_rowcol_at(self.__begin + self.__len) )
    
    def to_row(self):
        """
        Returns a pair of rows (integers).
        The first one means row of a charactor at the begin of this range.
        The second one means the end of this range.
        """
        ns_get_rowcol_at = self.__namedstr.get_rowcol_at
        return ( ns_get_rowcol_at(self.__begin), ns_get_rowcol_at(self.__begin + self.__len) )
    
    def to_rowcol_str(self):
        """
        Returns a string which contains a name of string and a begin row-col and an end row-col on the string.
        """
        
        ns = self.__namedstr
        ns_get_rowcol_at = ns.get_rowcol_at
        rcBegin = ns_get_rowcol_at(self.__begin)
        rcEnd = ns_get_rowcol_at(self.__begin + self.__len)
        return "%s %d.%d-%d.%d" % ( ns.name, rcBegin.row, rcBegin.col, rcEnd.row, rcEnd.col )
    
    def to_row_str(self):
        """
        Returns a string which contains a name of string and a begin row and an end row on the string.
        When the begin row and end row is the same value, the end row will be omitted.
        """
        ns = self.__namedstr
        ns_get_rowcol_at = ns.get_rowcol_at
        rcBegin = ns_get_rowcol_at(self.__begin)
        rcEnd = ns_get_rowcol_at(self.__begin + self.__len)
        if rcBegin.row == rcEnd.row:
            return "%s %d" % ( ns.name, rcBegin.row )
        else:
            return "%s %d-%d" % ( ns.name, rcBegin.row, rcEnd.row )

    def is_appendable(self, right):
        """
        Returns true, if this range appears just before the right range.
        """
        return self.__namedstr is right.__namedstr and self.__begin + self.__len == right.__begin

    def __add__(self, right):
        assert self.is_appendable(right)
        return ReferenceRange(self.__namedstr, self.__begin, right.__begin + right.__len)
    
    @staticmethod
    def optimized(ranges):
        ranges = filter(None, ranges)
        if len(ranges) <= 1: return ranges
        r = [ ranges[0] ]
        for cur in ranges[1:]:
            if r[-1].is_appendable(cur):
                r[-1] = ReferenceRange(r[-1].__namedstr, r[-1].__begin, cur.__begin + cur.__len)
            else:
                r.append(cur)
        return r

    def __eq__(self, right):
        return self.__namedstr is right.__namedstr and self.__begin == right.__begin and self.__len == right.__len
    
    def __ne__(self, right):
        return not (self.__namedstr is right.__namedstr and self.__begin == right.__begin and self.__len == right.__len)
    
    def __hash__(self):
        return self.__namedstr.__hash__() + self.__begin + self.__len
    
    def to_string(self):
        return self.namedstr[self.__begin:self.__begin + self.__len]
    
    def trimed(self, delta):
        #assert abs(delta) <= self.__end - self.__begin
        if delta > 0:
            return ReferenceRange(self.__namedstr, self.__begin + delta, self.__begin + self.__len)
        elif delta < 0:
            return ReferenceRange(self.__namedstr, self.__begin, self.__begin + self.__len + delta)
        else:
            return self
        assert False # must not reach here
    
    def extracted(self, delta):
        #assert abs(delta) <= self.__end - self.__begin
        if delta >= 0:
            return ReferenceRange(self.__namedstr, self.__begin, self.__begin + delta)
        elif delta < 0:
            end = self.__begin + self.__len
            return ReferenceRange(self.__namedstr, end + delta, end)
        assert False # must not reach here
    
class OriginedStrAttributeError(AttributeError):
    def __init__(self, message):
        AttributeError.__init__(self, message)

    
